this.name			= "PlanetFall";
this.author			= "Thargoid, with orientation bits nicked from Eric Walch and Frame";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike.";
this.description		= "Enables planetary landing";
this.version		= "1.2";

this.startUp = function()
	{ 
	log(this.name + " " + this.version + " loaded.");
	this.reset();
	}

this.setPos = function(obj,pos)
	{ 
	if (oolite.compareVersion('1.73') <= 0) 
		obj.position = pos; 
	else 
		obj.setPosition(pos); 
	}

this.setOri = function(obj,ori)
	{ 
	if (oolite.compareVersion('1.73') <= 0) 
		obj.orientation = ori; 
	else 
		obj.setOrientation(ori); 
	}

this.turnStation = function(tempStation)
	{
	let stationVector = tempStation.position.subtract(this.lastPlanet.position).direction(); // unit vector pointing away from the planet
	let angle = tempStation.heading.angleTo(stationVector); // angle between current heading and target heading
	let cross = tempStation.heading.cross(stationVector).direction(); // set the plane where we should rotate in
	this.setOri(tempStation, tempStation.orientation.rotate(cross, -angle)); // re-orient the station away from the planet
	}


this.reset = function() // define some OXP variables and set them to default values. They will change during approach.
	{
	this.lastStation = system.mainStation; // last station to be docked with
	this.lastPlanet = system.mainPlanet; // details of the last planet to be landed on, so we know what to move the player away from on launch
	this.lastPlanetType = "null"; // what type of planet/moon we landed on
	this.planetCount = 1; // default setting
	this.moonCount = 0; // default setting
	this.systemScan; // set up the initial planet and moon count
	this.planetApproach = false; // flag to stop multiple messages on planetary approach
	this.planetFallOverride = false; // flag to allow other OXPs to override PlanetFall locations

/* For convenience of mission writers, access for other OXPs is via "worldScripts.PlanetFall.lastPlanet" and "worldScripts.PlanetFall.lastPlanetType". 
Values for lastPlanetType are "Prime" for the main planet, "Sub" for every other planet, "Moon" for any moon and "Sun" / "GasGiant" for a sun or a Solar System OXP 
gas giant (for completeness - cannot land on either of the last two). */

	}

this.shipApproachingPlanetSurface = function(planet) 
	{

	this.lastPlanet = planet;

	if(!player.ship.hasEquipment("EQ_PLANETFALL"))
		{
		return;
		}

	if(!this.landingTimer)  
		{
		if(!planet.isSun && !planet.hasOwnProperty("solarGasGiant"))
			{
			if(planet == system.mainPlanet)
				{
				this.lastPlanetType = "Prime";
				this.landingChoice = (Math.random()*10);
				if(this.planetFallOverride)
					{
					this.port = player.ship.spawnOne("planetFall_mainSurface_externalOXP");
					this.turnStation(this.port);
					}
				else
					{
					if(this.landingChoice < 3) // spawn capital city
						{
						this.port = player.ship.spawnOne("planetFall_mainSurface_capitalCity"); 
						this.turnStation(this.port);
						} 
					if(this.landingChoice >= 3 && this.landingChoice < 4.5) // spawn external OXP location, or if none then Capital City
						{
						this.port = player.ship.spawnOne("planetFall_mainSurface_externalOXP"); 
						this.turnStation(this.port);
						} 
					if(this.landingChoice >= 4.5 && this.landingChoice < 6.5) // spawn a shipyard
						{
						this.port = player.ship.spawnOne("planetFall_mainSurface_shipYard");
						this.turnStation(this.port);
						} 
					if(this.landingChoice >= 6.5 && this.landingChoice < 7.5) // spawn a military base
						{
						this.port = player.ship.spawnOne("planetFall_mainSurface_militaryBase"); 
						this.turnStation(this.port);
						} 
					if(this.landingChoice >= 7.5 && this.landingChoice < 8.9) // spawn seedy bar or leisure complex
						{
						if(system.government == 0 || this.landingChoice < 8.05)
							{
							this.port = player.ship.spawnOne("planetFall_mainSurface_seedyBar");
							this.turnStation(this.port);
							this.barName = expandDescription('[planetFall_barName]');
							missionVariables.random_hits_seedy_spacebar_name = "********** You are viewing " + this.barName + " Hunters' Bulletin Board. *********";
							this.port.displayName = this.barName;
							}
						else
							{
							this.port = player.ship.spawnOne("planetFall_mainSurface_leisureComplex");
							this.turnStation(this.port);
							}
						} 
					if(this.landingChoice >= 8.9 && this.landingChoice < 9.8) // spawn factory or farm
						{
						if(system.economy < 4)
							{
							this.port = player.ship.spawnOne("planetFall_mainSurface_factory");
							this.turnStation(this.port);
							}
						else
							{
							this.port = player.ship.spawnOne("planetFall_mainSurface_farm");
							this.turnStation(this.port);
							}
						} 
					if(this.landingChoice >= 9.8) // spawn open fields or waste dump
						{
						if(system.economy < 4)
							{
							this.port = player.ship.spawnOne("planetFall_mainSurface_dump");
							this.turnStation(this.port);
							}
						else
							{
							this.port = player.ship.spawnOne("planetFall_mainSurface_fields");
							this.turnStation(this.port);
							}
						} 
					}
				this.setPos(this.port,player.ship.position.add(player.ship.orientation.vectorForward().multiply(10))) // put the station 10m in front of the player
				this.port.displayName = system.name + " Prime - " + this.port.displayName; // set the name to the surface of the prime planet in the system
				}
			else
				{
				if(planet.hasAtmosphere)
					{
					this.lastPlanetType = "Sub";
					this.landingChoice = (Math.random()*10);
					if(this.planetFallOverride)
						{
						this.port = player.ship.spawnOne("planetFall_subSurface_externalOXP");
						this.turnStation(this.port);
						}
					else
						{
						if(this.landingChoice < 3) // spawn capital city
							{
							this.port = player.ship.spawnOne("planetFall_subSurface_colony"); 
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 3 && this.landingChoice < 4.5) // spawn external OXP location, or if none then a colony
							{
							this.port = player.ship.spawnOne("planetFall_subSurface_externalOXP"); 
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 4.5 && this.landingChoice < 6.5) // spawn a shipyard
							{
							this.port = player.ship.spawnOne("planetFall_subSurface_shipYard");
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 6.5 && this.landingChoice < 7.5) // spawn a military base
							{
							this.port = player.ship.spawnOne("planetFall_subSurface_militaryBase"); 
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 7.5 && this.landingChoice < 8.9) // spawn seedy bar or leisure complex
							{	
							if(system.government == 0 || this.landingChoice < 8.05)
								{
								this.port = player.ship.spawnOne("planetFall_subSurface_seedyBar");
								this.turnStation(this.port);
								this.barName = expandDescription('[planetFall_barName]');
								missionVariables.random_hits_seedy_spacebar_name = "********** You are viewing " + this.barName + " Hunters' Bulletin Board. *********";
								this.port.displayName = this.barName;
								}
							else
								{
								this.port = player.ship.spawnOne("planetFall_subSurface_leisureComplex");
								this.turnStation(this.port);
								}
							} 
						if(this.landingChoice >= 8.9 && this.landingChoice < 9.8) // spawn factory or farm
							{
							if(system.economy < 4)
								{
								this.port = player.ship.spawnOne("planetFall_subSurface_factory");
								this.turnStation(this.port);
								}
							else
								{
								this.port = player.ship.spawnOne("planetFall_subSurface_farm");
								this.turnStation(this.port);
								}
							} 
						if(this.landingChoice >= 9.8) // spawn open fields or waste dump
							{
							if(system.economy < 4)
								{
								this.port = player.ship.spawnOne("planetFall_subSurface_dump");
								this.turnStation(this.port);
								}
							else
								{
								this.port = player.ship.spawnOne("planetFall_subSurface_fields");
								this.turnStation(this.port);
								}
							} 
						}
					this.setPos(this.port,player.ship.position.add(player.ship.orientation.vectorForward().multiply(10))) // put the station 10m in front of the player
					this.port.displayName = system.name + " Minor - " + this.port.displayName; // set the name to just the surface, so we can identify easily the main planet
					}
				else
					{
					this.lastPlanetType = "Moon";
					this.landingChoice = (Math.random()*10);
					if(this.planetFallOverride)
						{
						this.port = player.ship.spawnOne("planetFall_moonSurface_externalOXP");
						this.turnStation(this.port);
						}
					else
						{
						if(this.landingChoice < 3) // spawn capital city
							{
							this.port = player.ship.spawnOne("planetFall_moonSurface_dome"); 
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 3 && this.landingChoice < 4.5) // spawn external OXP location, or if none then a dome
							{
							this.port = player.ship.spawnOne("planetFall_moonSurface_externalOXP"); 
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 4.5 && this.landingChoice < 6.5) // spawn a mine
							{
							this.port = player.ship.spawnOne("planetFall_moonSurface_mine");
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 6.5 && this.landingChoice < 8) // spawn a bar or leisure dome
							{	
							if(system.government == 0 || this.landingChoice < 7.5)
								{
								this.port = player.ship.spawnOne("planetFall_moonSurface_seedyBar");
								this.turnStation(this.port);
								this.barName = expandDescription('[planetFall_barName]');
								missionVariables.random_hits_seedy_spacebar_name = "********** You are viewing " + this.barName + " Hunters' Bulletin Board. *********";
								this.port.displayName = this.barName;
								}
							else
								{
								this.port = player.ship.spawnOne("planetFall_moonSurface_leisureDome");
								this.turnStation(this.port);
								}
							} 
						if(this.landingChoice >= 8 && this.landingChoice < 8.9) // spawn a prison
							{
							this.port = player.ship.spawnOne("planetFall_moonSurface_prison"); 
							this.turnStation(this.port);
							} 
						if(this.landingChoice >= 8.9 && this.landingChoice < 9.8) // spawn a research complex or factory
							{
							if(system.techLevel > 11)
								{
								this.port = player.ship.spawnOne("planetFall_moonSurface_researchComplex");
								this.turnStation(this.port);
								}
							else
								{
								this.port = player.ship.spawnOne("planetFall_moonSurface_factory");
								this.turnStation(this.port);
								}
							} 
						if(this.landingChoice >= 9.8) // spawn wasteland
							{
							this.port = player.ship.spawnOne("planetFall_moonSurface_wasteland"); 
							this.turnStation(this.port);
							} 
						}
					this.setPos(this.port,player.ship.position.add(player.ship.orientation.vectorForward().multiply(10))) // put the station 10m in front of the player
					this.port.displayName = "Moon of " + system.name + " - " + this.port.displayName; // set the name to the moon, so we can identify it as a moon
					}
				}
			}
		else
			{
			if(planet.hasOwnProperty("solarGasGiant"))
				{
				this.lastPlanetType = "GasGiant";
				}
			else
				{
				this.lastPlanetType = "Sun";
				}
			}
		}
	else
		{
		player.commsMessage(system.name + " Planetary Control - Pull up, you have no landing clearance!", 6);
		player.commsMessage("Return to orbit and approach again.", 6);
		this.landingTimer.stop();
		delete this.landingTimer;
		}
	}

this.alertConditionChanged = function()
	{
	if(player.alertAltitude && !this.lastPlanet.isSun && !this.lastPlanet.hasOwnProperty("solarGasGiant"))
		{
		if(!player.ship.hasEquipment("EQ_PLANETFALL"))
			{
			player.commsMessage(system.name + " Planetary Control - Your ship is not equipped for planetfall.", 10);
			player.commsMessage("Do not attempt landing, your ship will be destroyed!", 10);
			}
		else
			{
			player.commsMessage(system.name + " Planetary Control - Landing clearance request received.", 6);
			player.commsMessage("Please await confirmation before beginning final approach.", 6);
			if(!this.landingTimer)
				{
				this.clearanceDelay = 5 + (Math.ceil(Math.random()*15)); // delay between 6 and 20 seconds
				this.landingTimer = new Timer(this, this.landingClearance, this.clearanceDelay);
				}
			else
				{
				this.landingTimer.start();
				}
			}
		}

	if(player.alertAltitude && this.lastPlanet.hasOwnProperty("solarGasGiant"))
		{
		player.consoleMessage("ALERT - You are approaching a Gas Giant. Landing here is not possible!", 10);
		}

	if(!player.alertAltitude && this.landingTimer && this.landingTimer.isRunning && !player.ship.docked && !player.ship.hasEquipment("EQ_FRAME_SAVE"))
		{
		this.landingTimer.stop();
		delete this.landingTimer;
		missionVariables.planetFallBreak += 0.01; // 1% cumulative chance of equipment failure
		player.commsMessage(system.name + " Planetary Control - Landing clearance request cancelled.", 6);
		player.commsMessage("Goodbye Commander " + player.name + ".", 6);
		}
	}

this.landingClearance = function()
	{
	if(this.landingTimer) {
		this.landingTimer.stop();
		delete this.landingTimer;
	}
	player.commsMessage(system.name + " Planetary Control - Landing clearance granted.",6);
	player.commsMessage("Please approach the surface slowly.", 6);
	}

this.shipLeavingPlanetSurface = function(planet)
	{
	this.lastPlanet = planet;
	this.shipDied(); // don't panic, just saving code duplication to clean up the system... ;)
	}

this.shipDied = function()
	{
	let ports = system.shipsWithRole("planetFall_surface"); // an array of any rogue "surface stations" in the system
	this.portCount = ports.length; // how many rogues we have that need to be removed
	if(this.portCount > 0) for (let i=0; i<this.portCount; i++) // if there are any, loop through and remove them
		{
		this.removePort(ports[i]);
		}
	}

this.planetScan = function()
	{
	function isGlobe(entity) 
		{
		return (entity.isPlanet || entity.isSun || entity.hasOwnProperty("solarGasGiant"));
		}

	this.globes = system.filteredEntities(this, isGlobe, player.ship)

	if(system.isInterstellarSpace)
		{
		return;
		}

	this.globeRange = player.ship.position.distanceTo(this.globes[0]) + this.globes[0].radius;

	if(this.globeRange < this.globes[0].radius * 3 && !this.planetApproach)	
		{
		this.lastPlanet = this.globes[0];
		this.planetApproach = true;
		if(!this.globes[0].isSun && !this.globes[0].hasOwnProperty("solarGasGiant") && player.ship.hasEquipment("EQ_PLANETFALL"))
			{
			player.commsMessage(system.name + " Planetary Control - for planetfall please approach and request landing clearance.",5);
			}
		else
			{
			if(this.globes[0].isSun)
				{
				player.consoleMessage("ALERT - Approaching the Sun. Temperature monitoring is critical!", 10);
				}
			if(this.globes[0].hasOwnProperty("solarGasGiant"))
				{
				player.consoleMessage("ALERT - You are approaching a Gas Giant. Landing here is not possible!", 10);
				}
			}
		}

	if(this.globeRange > this.globes[0].radius * 3 && this.planetApproach)	
		{
		this.planetApproach = false;	
		}
	}

this.shipExitedPlanetaryVicinity = function()
	{
	this.planetScan();
	if(this.rangeCheckTimer)
		{
		this.rangeCheckTimer.start();
		}
	else
		{
		this.rangeCheckTimer = new Timer(this, this.planetScan, 0, 15);
		}
	}

this.shipWillLaunchFromStation = function(station)
	{
	if(station)
		{
		this.lastStation = station;
		}
	}

this.shipLaunchedFromStation = function()
	{
	if(player.ship.hasEquipment("EQ_FRAME_SAVE")) // if launch is for Save Anywhere, don't want to move the player or nuke the station
		{
		return;
		}

	if(this.rangeCheckTimer)
		{
		this.rangeCheckTimer.start();
		}
	else
		{
		this.rangeCheckTimer = new Timer(this, this.planetScan, 0, 15);
		}

	if(this.lastStation && this.lastStation.hasRole("planetFall_surface"))
		{
		if(this.landingTimer) 
			{ 
			this.landingTimer.stop();
			delete this.landingTimer;
			}		
		if(this.lastStation.hasRole("planetFall_mainSurface_seedyBar") || this.lastStation.hasRole("planetFall_subSurface_seedyBar") || this.lastStation.hasRole("planetFall_moonSurface_seedyBar"))
			{
			missionVariables.random_hits_docked = null;
			}
		let launchVector = player.ship.position.subtract(this.lastPlanet.position).direction(); // unit vector pointing away from the planet
		let launchPosition = player.ship.position.add(launchVector.multiply(2000)); // adds a 2km offset away from the planet
		let angle = player.ship.heading.angleTo(launchVector); // angle between current heading and target heading
		let cross = player.ship.heading.cross(launchVector).direction(); // set the plane where we should rotate in
		this.setOri(player.ship, player.ship.orientation.rotate(cross, -angle)); // re-orient the player ship away from the planet
		this.setPos(player.ship,launchPosition); // move the player away from the planet, make things a bit safer when they take flight control
		player.commsMessage(system.name + " Planetary Control - orbital boost complete.",5);
		player.commsMessage("Farewell Commander " + player.name + ".",5);
		this.removePort(this.lastStation); // remove the surface-simulating station
		missionVariables.planetFallBreak += 0.02; // 2% cumulative chance of equipment failure 
		if(Math.random() < missionVariables.planetFallBreak) // landing equipment needs maintenance
			{
			player.ship.setEquipmentStatus("EQ_PLANETFALL", "EQUIPMENT_DAMAGED");
			player.consoleMessage("ALERT - Planetary landing modifications require urgent maintenance.", 10);
			player.consoleMessage("Do not attempt to land again before they are repaired!", 10);
			missionVariables.planetFallBreak = -0.04; // first two launches guaranteed not to break equipment
			}
		}

	this.systemScan();
	}

this.playerBoughtEquipment = function(equipment)  // guarantee first two launches won't break equipment
	{
	if(equipment == "EQ_PLANETFALL")
		{
		missionVariables.planetFallBreak = -0.04;
		}
	}

this.shipWillDockWithStation = function(station)
	{
	if(this.rangeCheckTimer) {
		this.rangeCheckTimer.stop();
		delete this.rangeCheckTimer;
	}
	if(station.hasRole("planetFall_mainSurface_seedyBar") || station.hasRole("planetFall_subSurface_seedyBar") || station.hasRole("planetFall_moonSurface_seedyBar"))
		{ // Player is docked at a planet-side seedy bar
		missionVariables.random_hits_docked = true;
		}
	}

this.playerWillEnterWitchspace = function() {
    if (this.landingTimer) {
		this.landingTimer.stop();
		delete this.landingTimer;
	}
    if (this.rangeCheckTimer) {
		this.rangeCheckTimer.stop();
		delete this.rangeCheckTimer;
	}
};

this.shipExitedWitchspace = function()
{
	this.systemScan();
	this.planetScan();
	if(this.rangeCheckTimer) {
		this.rangeCheckTimer.start();
	} else {
		this.rangeCheckTimer = new Timer(this, this.planetScan, 0, 15);
	}
}

this.systemScan = function()
	{
	function isMoon(entity) {return (entity.isPlanet && !entity.hasAtmosphere) };
	this.systemArray = system.planets;
	this.systemCount = this.systemArray.length;
	this.moonCount = system.filteredEntities(this.systemArray, isMoon).length;
	this.planetCount = this.systemCount - this.moonCount;
	}

this.removePort = function(port)
	{	
	if(port.hasRole("planetFall_surface"))
		{
		port.remove(); 
		};
	}

this.guiScreenChanged = function()
	{
	if(!player.ship.docked) // for GUI screen changes whilst in flight, which we can ignore
		{ return; }

	if(!player.ship.dockedStation.hasRole("planetFall_surface")) // if we're at a trunk or other OXP's station
		{ return; }

	 // Player is docked at a military base or penal colony - not somewhere a criminal should make himself known ;)
	if(player.ship.bounty > 25) // player is mid-level offender or fugitive
		{
			if(player.ship.dockedStation.hasRole("planetFall_mainSurface_militaryBase") || player.ship.dockedStation.hasRole("planetFall_subSurface_militaryBase") || player.ship.dockedStation.hasRole("planetFall_moonSurface_prison"))
			{
				if(guiScreen == "GUI_SCREEN_EQUIP_SHIP" || guiScreen == "GUI_SCREEN_MARKET" || guiScreen == "GUI_SCREEN_SHIPYARD")
				{
				missionVariables.planetFallFine = 20 + (Math.ceil(Math.random()*480)); // fine of 21-500 credits
				if(player.ship.bounty > 50) // if the player is a fugitive
					{
					missionVariables.planetFallFine += (Math.ceil(Math.random()*500)); // raise fine by another 1-500 credits
					}
				mission.runMissionScreen("planetFall_prison", "planetFall_cell.png", null, null, null);
				if(player.credits > missionVariables.planetFallFine)
					{
					player.credits -= missionVariables.planetFallFine;
					player.ship.bounty -= (Math.ceil(missionVariables.planetFallFine * 0.05)); // lower bounty corresponding to fine.
					}
				else
					{
					player.credits = 10;
					}
				}
			}
		}

	if(!player.ship.dockedStation.hasRole("planetFall_noTrade")) // if we're at a location where trading is possible
		{ return; }

	// no-one to trade with on the empty lunar surface
	if(player.ship.dockedStation.hasRole("planetFall_moonSurface_wasteLand") ) 
		{
		if(guiScreen == "GUI_SCREEN_EQUIP_SHIP" || guiScreen == "GUI_SCREEN_MARKET")
			{
			mission.runMissionScreen("planetFall_noTrade", "planetFall_lunarScape.png", null, null, null);
			}
		}

	 // no-one to trade with in empty fields
	if(player.ship.dockedStation.hasRole("planetFall_mainSurface_fields") || player.ship.dockedStation.hasRole("planetFall_subSurface_fields")) 
		{
		if(guiScreen == "GUI_SCREEN_EQUIP_SHIP" || guiScreen == "GUI_SCREEN_MARKET")
			{
			mission.runMissionScreen("planetFall_noTrade", "planetFall_fields.png", null, null, null);
			}
		}

	// no-one to trade with in refuse dump
	if(player.ship.dockedStation.hasRole("planetFall_mainSurface_dump") || player.ship.dockedStation.hasRole("planetFall_subSurface_dump")) 
		{
		if(guiScreen == "GUI_SCREEN_EQUIP_SHIP" || guiScreen == "GUI_SCREEN_MARKET" || guiScreen == "GUI_SCREEN_SHIPYARD")
			{
			mission.runMissionScreen("planetFall_noTrade", "planetFall_dump.png", null, null, null);
			}
		}
	}